package fr.jade.fraclite.type;

import java.io.Serializable;

import org.objectweb.fractal.api.NoSuchInterfaceException;
import org.objectweb.fractal.api.Type;
import org.objectweb.fractal.api.factory.InstantiationException;
import org.objectweb.fractal.api.type.ComponentType;
import org.objectweb.fractal.api.type.InterfaceType;

import fr.jade.fraclite.ChainedNoSuchInterfaceException;

/**
 * Provides a basic implementation of the {@link ComponentType} interface.
 */
public class BasicComponentType implements ComponentType, Serializable {

  /**
   * The types of the interfaces of components of this type.
   */
  private final InterfaceType[] interfaceTypes;

  // -------------------------------------------------------------------------
  // Constructors
  // -------------------------------------------------------------------------

  /**
   * Constructs a {@link BasicComponentType} object.
   *
   * @param itfTypes the types of the interfaces of components of this type.
   * @throws InstantiationException if two interface types have the same name,
   *      or if the name of an interface type is a prefix of the name of a
   *      collection interface type.
   */
  public BasicComponentType (final InterfaceType[] itfTypes)
    throws InstantiationException
  {
    interfaceTypes = clone(itfTypes);
    // verifications
    for (int i = 0; i < interfaceTypes.length; ++i) {
      String p = interfaceTypes[i].getFcItfName();
      boolean collection = interfaceTypes[i].isFcCollectionItf();
      for (int j = i + 1; j < interfaceTypes.length; ++j) {
        String q = interfaceTypes[j].getFcItfName();
        if (p.equals(q)) {
          throw new InstantiationException("Two interfaces have the same name '" + q + "'");
        }
        if (collection && q.startsWith(p)) {
          throw new InstantiationException("The name of the interface '" + q + "' starts with '" +
            p + "', which is the name of a collection interface");
        }
        if (interfaceTypes[j].isFcCollectionItf() && p.startsWith(q)) {
          throw new InstantiationException("The name of the interface '" + p + "' starts with '" +
            q + "', which is the name of a collection interface");
        }
      }
    }
  }

  // -------------------------------------------------------------------------
  // Implementation of the ComponentType interface
  // -------------------------------------------------------------------------

  public InterfaceType[] getFcInterfaceTypes () {
    return clone(interfaceTypes);
  }

  public InterfaceType getFcInterfaceType (final String name)
    throws NoSuchInterfaceException
  {
    for (int i = 0; i < interfaceTypes.length; ++i) {
      InterfaceType itfType = interfaceTypes[i];
      String itfName = itfType.getFcItfName();
      if (itfType.isFcCollectionItf()) {
        if (name.startsWith(itfName)) {
          return itfType;
        }
      } else if (name.equals(itfName)) {
        return itfType;
      }
    }
    throw new ChainedNoSuchInterfaceException(null, null, name);
  }

  public boolean isFcSubTypeOf (final Type type) {
    try {
      if (type instanceof ComponentType) {
        ComponentType t = (ComponentType)type;
        InterfaceType[] itfs = interfaceTypes;
        for (int i = 0; i < itfs.length; ++i) {
          InterfaceType i1 = itfs[i];
          if (i1.isFcClientItf()) {
            InterfaceType i2 = t.getFcInterfaceType(i1.getFcItfName());
            if (!i1.isFcSubTypeOf(i2)) {
              return false;
            }
          }
        }
        itfs = t.getFcInterfaceTypes();
        for (int i = 0; i < itfs.length; ++i) {
          InterfaceType i2 = itfs[i];
          if (!i2.isFcClientItf()) {
            InterfaceType i1 = getFcInterfaceType(i2.getFcItfName());
            if (!i1.isFcSubTypeOf(i2)) {
              return false;
            }
          }
        }
        return true;
      }
    } catch (NoSuchInterfaceException e) {
    }
    return false;
  }

  /**
   * Returns a copy of the given interface type array. This method is used to
   * return a copy of the field of this class, instead of the field itself, so
   * that its content can not be modified from outside.
   *
   * @param types the array to be cloned.
   * @return a clone of the given array, or an empty array if <tt>type</tt> is
   *      <tt>null</tt>.
   */

  private static InterfaceType[] clone (final InterfaceType[] types) {
    if (types == null) {
      return new InterfaceType[0];
    } else {
      InterfaceType[] clone = new InterfaceType[types.length];
      System.arraycopy(types, 0, clone, 0, types.length);
      return clone;
    }
  }
  
  public String toString(){
    String str = "[";
    for(InterfaceType it : interfaceTypes) str = str + it.toString() + " - "; 
    return str+"]";
  }
}
